/*
Authors: Eli Yucht 300388949 and Lior Fass 300069705
Project: Hamming
Description: Sender class implementation
*/
#include "sender.h"


// initialize sender object fields
Sender::Sender()
{
	file_p = NULL;
	sender_socket = INVALID_SOCKET;
	
}
// free sender object fields (when finish)
Sender::~Sender()
{

	if (file_p != NULL)
		fclose(file_p);
	closesocket(sender_socket);
	WSACleanup();
	
}
/* Initialize the sender:
			1. check the arguments for sender
			2. open the input file
			3. try to connect to the channel


*/
int Sender::Init(int argc, char *argv[])
{
	int status = Check_Arguments(argc, argv);
	if (status)
	{
		cerr << "args Check Failed" << endl;
		return 1;
	}

	file_p = fopen(argv[3], "rb");
	if (file_p == NULL)
	{
		cerr << "Input File open failed\n";
		return 1;
	}

	sender_socket = Socket_tool::Connect_Channel(argv[1], (unsigned short)atoi(argv[2]));
	if (sender_socket == INVALID_SOCKET)
	{
		cerr << "Failed to connect to Channel" << endl;
		return 1;
	}

	return 0; // Great success obj initialized
}

/* check the sender's arguments:
	1. check vaild argc
	2. check valid ip address
	3. check valid port number


*/
int Sender::Check_Arguments(int argc, char *argv[])
{
	if (argc != NUM_OF_ARGS + 1)
	{
		cerr << "Invalid number of arguments for sender\n";
		return 1;
	}
	unsigned long ip_addr = inet_addr(argv[1]);

	if (ip_addr == INADDR_NONE)
	{
		cerr << "Invalid ip address\n";
		return 1;
	}

	if ((atoi(argv[2]) < 0) | (atoi(argv[2]) > 65536))
	{
		cerr << "Invalid port number (should be a number between 0-65536)\n";
		return 1;
	}
	return 0; // Arg Passed
}

/* Run the sender (after initialize it):

	1. Read the file and encode every 26 bit block to 31 bit hamming block
	2. Send the encoded file (compressed) to the channel socket
	3. Wait and print the response from the receiver

*/
int Sender::Run()
{
	int status = 0;


	status = Read_File_Encode_Send();
	if (status)
	{
		cout << "Failed in reading/sending input file" << endl;
		return 1;
	}

	status = Get_Response_From_Receiver();
	if (status)
	{
		cout << "Failed to get response from receiver" << endl;
		return 1;
	}


	return 0;
}

/* Read the input file encode it and send it over the channel

	1. read every time 26 bytes (or 13 bytes in the last read) from the file (until EOF reached)
	2. convert the bytes (that have been read) to 26 bit blocks and encode each 26 bit block to 31 bit hamming block
	3. compresse the 31 bit hamming blocks to bytes and send them on the channel (TCP) socket

*/
int Sender::Read_File_Encode_Send()
{
	unsigned char buffer_bytes[26];
	unsigned char hamming_block[32];

	unsigned char bytes_to_send[31];
	unsigned char last_bytes_to_send[16];


	while (!feof(file_p))
	{
		size_t elements_read = fread(buffer_bytes, 1, 26, file_p);
		if ((elements_read < 26) && (!feof(file_p)))
		{
			cerr << "File read failed\n";
			return 1;
		}
		else if (elements_read == 13) // we have reached to
		{							  //the last 13 bytes in the file

			//convert the last 13 bytes (in buffer_bytes) to 4 blocks of 26 bits each
			Bytes_Man::Bytes_to_26_bits(buffer_bytes, hamming_block);
			// encode 4 blocks with hamming code
			Hamming_EDM::encode_Blocks(hamming_block, 4);

			Bytes_Man::Block31_to_Bytes(hamming_block, last_bytes_to_send, 4);

			if (Socket_tool::SendBuffer((char*)last_bytes_to_send, 16, sender_socket) != TRNS_SUCCEEDED)	
				return 1;
		

		}
		else if (elements_read == 26)
		{
			//convert the first 13 bytes (in buffer_bytes) to 4 blocks of 26 bits each
			Bytes_Man::Bytes_to_26_bits(buffer_bytes, hamming_block);
			//convert the rest 13 bytes (in buffer_bytes) to 4 blocks of 26 bits each
			Bytes_Man::Bytes_to_26_bits(buffer_bytes + 13, hamming_block + 16);
			// encode 8 blocks with hamming code
			Hamming_EDM::encode_Blocks(hamming_block, 8);


			Bytes_Man::Block31_to_Bytes(hamming_block, bytes_to_send, 8);

			if (Socket_tool::SendBuffer((char*)bytes_to_send, 31, sender_socket) != TRNS_SUCCEEDED)
				return 1;
			
				
		}

	}
	return 0;
}

/* Wait and print the response from the receiver

		1. Shutdown the send function on the channel socket
		2. Receive the receiver response and print it to stderr

*/
	
int Sender::Get_Response_From_Receiver()
{
	int iResult = shutdown(sender_socket, SD_SEND);
	if (iResult == SOCKET_ERROR)
	{
		cerr << "shutdown for send socket failed: " << WSAGetLastError() << "\n";
		return 1;
	}
	// get response from receiver
	char * recvbuf = NULL;
	if (Socket_tool::ReceiveString(&recvbuf, sender_socket) == TRNS_SUCCEEDED)
	{
		cerr << recvbuf;
		free(recvbuf);
		return 0;
	}
	else
	{
		cerr << "Error at receiveing response from receiver\n";
		return 1;
	}
		
}



